﻿/****************************************************************************************
 * MainWindow.xaml.cs
 * 
 * Code behind the main window
 * Asghar Panahy
 * 05-jan-2011
 * http://stylishdosbox.codeplex.com
 ****************************************************************************************/
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.IO;

namespace WpfCmd
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// A simple window that uses CmdProcess
    /// <remarks>Use LeftShift down with the mousewheal to change opacity</remarks>
    /// <remarks>Use LeftCtrl down with mousewheal to change the zoom factor</remarks>    
    /// <remarks>Use LeftCtrl down with mouseLeftButtonDown to move the screen </remarks>
    /// </summary>    
    public partial class MainWindow : Window
    {
        #region Private Fields
        /// <summary>The background process</summary>
        private CmdProcess cmd = new CmdProcess();

        /// <summary>
        /// Helps to remember the executed commands
        /// </summary>
        private CommandHistory commandHistory = new CommandHistory();

        /// <summary>
        /// helps to dump the lines into the rich text box
        /// </summary>
        private Paragraph paragraph = new Paragraph();

        /// <summary>
        /// flag indicating that user is zoming in or out.
        /// </summary>
        private bool isZomInAction = false;

        /// <summary>
        /// flag indicating that the user is changing the opacity
        /// </summary>
        private bool isOpacityInAction = false;

        #endregion

        /// <summary>
        /// Subscribe to the events of the cmd and mouse down
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            this.Closing += MainWindow_Closing;
            cmd.OutputDataReceived += OnOutputDataReceived;
            cmd.ErrorDataReceived += OnErrorDataReceived;
            cmd.Exited += OnExited;

            txtResult.Document.Blocks.Add(paragraph);
            txtResult.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(txtResult_PreviewMouseLeftButtonDown);

            string[] args = Environment.GetCommandLineArgs();
            // first parameter is this executable
            if (args.Length > 1) 
            {
                var command = string.Join(" ", args, 1, args.Length -1);
                var path = command.Trim("\"".ToCharArray());
                if (Directory.Exists(command))
                {
                    Directory.SetCurrentDirectory(command);
                    cmd.Execute("cd \"" + command + "\"");
                }
                else
                {
                    cmd.Execute(command);
                    commandHistory.Add(command);
                    PutCommandLine(command);
                }
            }

            txtCommand.Focus();

        }

        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            commandHistory.Save();
        }

        #region Command line messages
        #region Process Messages
        /// <summary>
        /// raised by the cmd process when output line has arrived.
        /// puts the output line into an Inline in the paragraph
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (ThreadStart)delegate
            {
                paragraph.Inlines.Add(new Run(e.Data));
                paragraph.Inlines.Add(new LineBreak());

                txtResult.ScrollToEnd();
            });
        }

        /// <summary>
        /// raised by the cmd process when an error line arrives
        /// puts the error line in a red foreground inline and adds it to the paragraph
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (ThreadStart)delegate
            {
                paragraph.Inlines.Add(new Run(e.Data)
                {
                    Foreground = Brushes.Red
                });
                paragraph.Inlines.Add(new LineBreak());

                txtResult.ScrollToEnd();
            });
        }

        /// <summary>
        /// raised by the cmd when it exits the execution
        /// Shows a message and disables the commandline text box
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnExited(object sender, EventArgs e)
        {
            // Dispatcher.Invoke(DispatcherPriority.Normal, new Action<string>(SetStatus), e.Message);
            Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (ThreadStart)delegate
            {
                paragraph.Inlines.Add(new Run("*** EXCITED ***"));
                paragraph.Inlines.Add(new LineBreak());

                txtResult.ScrollToEnd();
            });
            Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (ThreadStart)delegate
            {
                txtCommand.IsEnabled = false;
            });
        }

        #endregion

        /// <summary>
        /// raised by the input textbox 
        /// parses the content of the textbox for 'cls' and 'exit' commands to handle manually
        /// the rest of the commands will be passed to the cmd process per line
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCommandKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (ParseKnownCommands())
                    return;
                else
                {
                    cmd.Execute(txtCommand.Text);
                    commandHistory.Add(txtCommand.Text);
                    PutCommandLine(txtCommand.Text);
                }
            }
        }

        /// <summary>
        /// reads the text in the txtCommand and tries to parse the known command
        /// </summary>
        /// <returns>true if parsed, else false</returns>
        private bool ParseKnownCommands()
        {
            var command = txtCommand.Text.Trim();
            if (command.Equals("Exit", StringComparison.InvariantCultureIgnoreCase))
            {
                this.Close();
            }
            else if (txtCommand.Text.Equals("cls", StringComparison.InvariantCultureIgnoreCase))
            {
                txtResult.Document.Blocks.Clear();
                paragraph = new Paragraph();
                txtResult.Document.Blocks.Add(paragraph);
            }
            else if (txtCommand.Text.Equals("min", StringComparison.InvariantCultureIgnoreCase))
            {
                this.WindowState = WindowState.Minimized;
            }
            else if (txtCommand.Text.Equals("max", StringComparison.InvariantCultureIgnoreCase))
            {
                this.WindowState = WindowState.Maximized;
            }
            else if (txtCommand.Text.Equals("normal", StringComparison.InvariantCultureIgnoreCase))
            {
                this.WindowState = WindowState.Normal;
            }
            else
            {
                return false;
            }
            return true;
        }

        private void txtCommand_PreviewKeyDown(object sender, KeyEventArgs e)
        {

            if (e.Key == Key.Down)
            {
                if (commandHistory.HasNext)
                {
                    PutCommandLine(commandHistory.GetNext());
                }
                e.Handled = true;
            }
            else if (e.Key == Key.Up)
            {
                if (commandHistory.HasPrevious)
                {
                    PutCommandLine(commandHistory.GetPrevious());
                }
                e.Handled = true;
            }
            //else
            //{
            //    Debug.WriteLine(e.Key);
            //}
        }
        #endregion

        #region Look and Feel

        void txtResult_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (isZomInAction)
            {
                DragMove();
            }
        }

        private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (isZomInAction) ZoomDelta(e.Delta);
            if (isOpacityInAction) ChangeTransparency(e.Delta);
        }

        private void ZoomDelta(int delta)
        {
            if (delta > 0 && scaler.ScaleX < 4)
            { scaler.ScaleX += 0.05; scaler.ScaleY += 0.05; }
            else if (delta < 0 && scaler.ScaleX > 0.05)
            { scaler.ScaleX -= 0.05; scaler.ScaleY -= 0.05; }
        }
        private void ChangeTransparency(int delta)
        {
            if (delta > 0 && txtResult.Opacity < 1)
            {
                txtResult.Opacity += 0.05;
                this.Opacity += 0.05;
            }
            else if (delta < 0 && txtResult.Opacity > 0)
            {
                txtResult.Opacity -= 0.05;
                this.Opacity -= 0.05;
            }
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            isZomInAction = false;
            isOpacityInAction = false;
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            isOpacityInAction = e.IsDown && (e.Key == Key.LeftShift);
            isZomInAction = e.IsDown && (e.Key == Key.LeftCtrl);
        }

        #endregion
        
        private void PutCommandLine(string command)
        {
            var len = command.Length;
            txtCommand.Text = command;
            txtCommand.SelectionStart = 0;
            txtCommand.SelectionLength = len;
        }
    }
}
